/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.specific.sge.qbase;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import jp.aist.gtrc.plus.scheduler.specific.sge.jaxb.common.JAXBSGEJob;
import jp.aist.gtrc.plus.scheduler.specific.sge.jaxb.qstat.*;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEHostGroup;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEJobID;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEJobStatus;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEParallelEnv;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEProject;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEStatusManager;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGETaskStatus;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEUserset;
import jp.aist.gtrc.plus.scheduler.status.MainServer;
import jp.aist.gtrc.plus.scheduler.status.QueueStatus;
import jp.aist.gtrc.plus.scheduler.sys.SchedulerException;
import jp.aist.gtrc.plus.scheduler.util.CmdUtil;
import jp.aist.gtrc.plus.scheduler.util.CollectionUtil;

public final class SGEQbaseStatusManager extends SGEStatusManager {

	private String peNames;
	private Unmarshaller unmarshaller;
	private List<JAXBQSTATJob> pendingJobList = null;

	private static final SGEJAXBObjectBuilder builder = new SGEJAXBObjectBuilder();

	private SGEQbaseStatusManager() {

		super();

		try {
			/*
			 * NOTE: use qstat.ObjectFactory, not common.ObjectFactory!
			 */
			String pkg = jp.aist.gtrc.plus.scheduler.specific.sge.jaxb.qstat.
				ObjectFactory.class.getPackage().getName();
			JAXBContext context = JAXBContext.newInstance(pkg);
			unmarshaller = context.createUnmarshaller();
		} catch (JAXBException e) {
			unmarshaller = null;
		}

	}

	private void checkJAXB() throws SchedulerException {

		if (unmarshaller == null) {
			throw new SchedulerException("failed to init JAXB module");
		}

	}

	public static synchronized SGEStatusManager getInstance() {

		SGEStatusManager mgr = getInstanceForOverride();
		if (mgr == null){
			mgr = new SGEQbaseStatusManager();
			setInstance(mgr);
		}
		return mgr;

	}

	public synchronized void updateStatus(MainServer srv) throws SchedulerException{

		super.updateStatus(srv);

		SGEQbaseMainServer server = (SGEQbaseMainServer) srv;
		peNames = server.getPENames();

	}

	private JAXBSGEQstat getJAXBSGEQstat() throws SchedulerException {

		checkJAXB();

		LinkedList<String> stdout = CmdUtil.runCommand(new String[]{
				"qstat", "-f", "-u", "*", "-r", "-xml"});
		StringBuffer buf = new StringBuffer();
		for (String line : stdout) {
			buf.append(line);
		}

		InputStream in = new ByteArrayInputStream(buf.toString().getBytes());
		JAXBElement<?> elem;
		try {
			elem = (JAXBElement<?>)unmarshaller.unmarshal(in);
		} catch (JAXBException e) {
			throw new SchedulerException(e.getMessage());
		}
		Object o = elem.getValue();
		if (o instanceof JAXBSGEQstat) {
			return (JAXBSGEQstat)o;
		} else {
			throw new SchedulerException("got invalid object");
		}

	}

	protected void getAllJobs(MainServer srv) throws SchedulerException {

		jobsOnQins.clear();
		jobsOnNode.clear();

		JAXBSGEQstat qstat = getJAXBSGEQstat();
		JAXBQSTATQueueList qList = qstat.getQueueInfo();
		if (qList == null)
			return;

		for (JAXBQSTATQinstance qins : qList.getQueueList()) {
			Collection<JAXBQSTATJob> jaxbJobList = qins.getJobList();
			if (jaxbJobList.size() == 0) {
				continue;
			}

			String fullname = qins.getName();
			String[] name = fullname.split("@");
			if (name.length != 2) {
				assert(false);
				continue;
			}
			for (JAXBQSTATJob jaxbJob : jaxbJobList) {
				JAXBSGEJob jaxbJob2 = builder.createJob(jaxbJob, qins);
				SGEJobStatus job = new SGEJobStatus(jaxbJob2);
				// PluS always handles each "SGE task" as one "JobStatus"
				// even if this job has only one task.
				for (SGETaskStatus task : job.getAllTasks()) {
					addJob(task, fullname, name[1]);
				}
			}
		}

		// NOTE: not qstat.getJobList() is always null
		// use qstat.getJobInfo().getJobList() to get pending jobs(tasks).
		pendingJobList = qstat.getJobInfo().getJobList();

	}

	public synchronized String getPENames() {

		return peNames;

	}

	public synchronized boolean hasPendingJobs(QueueStatus queue) {

		if (CollectionUtil.isEmpty(pendingJobList)) {
			return false;
		}

		String tgtQname = queue.getName();
		for (JAXBQSTATJob job : pendingJobList) {
			Collection<String> reqQnames = job.getHardReqQueue();
			if (reqQnames == null) {
				continue;
			}

			for (String name : reqQnames) {
				if (name.equals(tgtQname)) {
					return true;
				}
			}
		}
		return false;

	}

	/*
	 * NOTE: These methods must not be called now!
	 */
	public HashMap<String, SGEUserset> getUsersetMap() {
		assert(false);
		return null;
	}

	public SGEUserset getUserset(String usersetName) {
		assert(false);
		return null;
	}

	public HashMap<String, SGEProject> getProjectMap() {
		assert(false);
		return null;
	}

	public SGEProject getProject(String projName) {
		assert(false);
		return null;
	}

	public HashMap<String, SGEParallelEnv> getParallelEnvMap() {
		assert(false);
		return null;
	}

	public SGEParallelEnv getParallelEnv(String peName) {
		assert(false);
		return null;
	}

	public HashMap<String, SGEHostGroup> getHostGroupMap() {
		assert(false);
		return null;
	}

	public SGEHostGroup getHostGroup(String hgroupName) {
		assert(false);
		return null;
	}

	public boolean isDirtyTask(SGEJobID jobID) {
		assert(false);
		return false;
	}

}
